package org.chen.config;

import com.alibaba.nacos.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.chen.model.AccessLogMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.net.InetSocketAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Slf4j
public class AccessLogFilter implements GlobalFilter, Ordered {

    @Value("${app.path-prefix}")
    private String pathPrefix;

    private Pattern articlePattern;

    @PostConstruct
    public void init() {
        // 动态构建正则表达式
        articlePattern = Pattern.compile("^" + pathPrefix + "/articles/([a-f0-9]{24})$");
    }

    private final RocketMQTemplate rocketMQTemplate;

    private static final String LOG_TOPIC = "article_access_log";

    public AccessLogFilter(RocketMQTemplate rocketMQTemplate) {
        this.rocketMQTemplate = rocketMQTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long startTime = System.currentTimeMillis();
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 只处理文章详情页
        Matcher matcher = articlePattern.matcher(path);
        if (!matcher.matches() || !HttpMethod.GET.equals(request.getMethod())) {
            return chain.filter(exchange);
        }

        String articleId = matcher.group(1);
        String ip = getIpAddress(request);
        String userAgent = request.getHeaders().getFirst(HttpHeaders.USER_AGENT);

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            try {
                // 发送访问日志消息
                sendAccessLogMessage(articleId, ip, userAgent, startTime);
            } catch (Exception e) {
                log.error("Process access log failed", e);
            }
        }));
    }

    @Override
    public int getOrder() {
        // 在 -1 到 1 之间的值，确保在其他过滤器之前执行
        // 数值越小优先级越高
        return -100;
    }

    /**
     * 获取真实IP地址
     * 依次从以下头信息中获取：
     * 1. X-Forwarded-For
     * 2. X-Real-IP
     * 3. Proxy-Client-IP
     * 4. WL-Proxy-Client-IP
     * 最后才使用远程地址
     */
    private String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = null;

        // 尝试从X-Forwarded-For获取
        String xForwardedFor = headers.getFirst("X-Forwarded-For");
        if (StringUtils.hasText(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            // 多个代理的情况，第一个IP为客户端真实IP
            int index = xForwardedFor.indexOf(",");
            if (index != -1) {
                ip = xForwardedFor.substring(0, index);
            } else {
                ip = xForwardedFor;
            }
        }

        // 尝试从X-Real-IP获取
        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }

        // 尝试从Proxy-Client-IP获取
        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }

        // 尝试从WL-Proxy-Client-IP获取
        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }

        // 如果以上都没有，使用远程地址
        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
            InetSocketAddress remoteAddress = request.getRemoteAddress();
            if (remoteAddress != null) {
                ip = remoteAddress.getAddress().getHostAddress();
            }
        }

        // 验证IP地址的合法性
        if (StringUtils.hasText(ip) && isValidIp(ip)) {
            return ip;
        }

        return "unknown";
    }

    /**
     * 验证IP地址的合法性
     */
    private boolean isValidIp(String ip) {
        try {
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                return false;
            }
            String[] parts = ip.split("\\.");
            if (parts.length != 4) {
                return false;
            }
            for (String part : parts) {
                int value = Integer.parseInt(part);
                if (value < 0 || value > 255) {
                    return false;
                }
            }
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 发送访问日志消息
     */
    private void sendAccessLogMessage(String articleId, String ip, String userAgent, long startTime) {
        AccessLogMessage message = AccessLogMessage.builder()
                .articleId(articleId)
                .ip(ip)
                .userAgent(userAgent)
                .timestamp(startTime)
                .duration((int)(System.currentTimeMillis() - startTime))
                .build();

        rocketMQTemplate.asyncSend(LOG_TOPIC, message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("Send access log message success");
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("Send access log message failed", throwable);
            }
        });
    }
}
